#pragma once

#include "Runnable.h"
#include "RunnableThread.h"
#include "IThreadWork.h"
#include "WorkThread.h"
#include <vector>
#include <mutex>
#include <condition_variable>
#include <future>

class WorkThread;

/**
 * class for thread pools.
 *
 * This interface is used by all queued thread pools. It used as a callback by
 * FQueuedThreads and is used to queue asynchronous work for callers.
 */
class ThreadPool
{
protected:

	/** The synchronization object used to protect access to the queued work. */
	std::mutex* mSynchQueue;

	/** If true, indicates the destruction process has taken place. */
	bool mTimeToDie;

	/** The work queue to pull from. */
	std::vector<IThreadWork*> mThreadWorks;

	/** The thread pool to dole work out to. */
	std::vector<WorkThread*> mWorkThreads;

	/** All threads in the pool. */
	std::vector<WorkThread*> mAllThreads;

	/** Use to wait  */
	std::shared_future<void> mStartFuture;

public:

	ThreadPool(std::shared_future<void>& startFuture);

	~ThreadPool();

	/**
	 * Creates the thread pool with the specified number of threads
	 *
	 * @param InNumQueuedThreads Specifies the number of threads to use in the pool
	 * @param StackSize The size of stack the threads in the pool need (32K default)
	 * @param ThreadPriority priority of new pool thread
	 * @return Whether the pool creation was successful or not
	 */
	bool Create(uint32 numQueuedThreads, ThreadPriority threadPriority = ThreadPriority::TPri_Normal);

	/** Tells the pool to clean up all background threads */
	void Destroy();

	/**
	 * Checks to see if there is a thread available to perform the task. If not,
	 * it queues the work for later. Otherwise it is immediately dispatched.
	 *
	 * @param InQueuedWork The work that needs to be done asynchronously
	 * @see RetractQueuedWork
	 */
	void AddThreadWork(IThreadWork* threadWork);

	/**
	 * Attempts to retract a previously queued task.
	 *
	 * @param InQueuedWork The work to try to retract
	 * @return true if the work was retracted
	 * @see AddQueuedWork
	 */
	bool RetractThreadWork(IThreadWork* threadWork);

	/**
	 * Get the number of queued threads
	 */
	uint32 GetNumThreads() const;

	/**
	 * this is a estimate of the number of queued jobs. 
	 */
	uint32 GetNumWorkJobs() const;

	//**
	// Get the new ThreadWork
	// Returns:   IThreadWork* the next ThreadWork
	// Parameter: WorkThread * workThread
	IThreadWork* ReturnToPoolOrGetNextJob(WorkThread* workThread);
};
